﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using FoodBank.Core.Models;
using FoodBank.Core.Repository;

namespace FoodBank.Data
{
    public class DummyDataRepository<T> : IRepository<T> where T : class
    {
        private readonly ICollection<Product> _products;
        private readonly ICollection<ProductCategory> _categories; 
        private readonly ICollection<User> _users;
        private readonly ICollection<Brand> _brands;
        private readonly ICollection<UserProductConsumption> _consumptions; 

        /// <summary>
        /// Gets the data list.
        /// </summary>
        private ICollection<T> DataList
        {
            get
            {
                if (typeof(T) == typeof(Product))
                    return (ICollection<T>)_products;

                if (typeof(T) == typeof(ProductCategory))
                    return (ICollection<T>)_categories;

                if (typeof(T) == typeof(User))
                    return (ICollection<T>)_users;

                if (typeof(T) == typeof(Brand))
                    return (ICollection<T>)_brands;

                if (typeof(T) == typeof(UserProductConsumption))
                    return (ICollection<T>)_consumptions;

                return null;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DummyDataRepository&lt;T&gt;"/> class.
        /// </summary>
        public DummyDataRepository()
        {
            _products = new List<Product>();
            _categories = new List<ProductCategory>();
            _users = new List<User>();
            _brands = new List<Brand>();
            _consumptions = new List<UserProductConsumption>();

            LoadDummyData();
        }

        #region IRepository Implementation

        /// <summary>
        /// Adds the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Add(T entity)
        {
            DataList.Add(entity);
        }

        /// <summary>
        /// Updates the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Update(T entity)
        {
            var updatedEntityId = GetReflectedIdValue(entity);

            T foundEntity = null;
            foreach (var exisitingEntity in DataList)
            {
                var exisitingEntityId = GetReflectedIdValue(exisitingEntity);
                if (exisitingEntityId == updatedEntityId)
                {
                    foundEntity = exisitingEntity;
                    break;
                }
            }
            DataList.Remove(foundEntity);
            DataList.Add(entity);
        }

        /// <summary>
        /// Deletes the specified entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Delete(T entity)
        {
            DataList.Remove(entity);
        }

        /// <summary>
        /// Deletes using the specified predicate.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        public void Delete(Expression<Func<T, bool>> predicate)
        {
            var result = DataList.Where(predicate.Compile());

            foreach (var entity in result)
            {
                Delete(entity);
            }
        }

        /// <summary>
        /// Retrieves the entity using the specified id.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        public T RetrieveById(int id)
        {
            foreach (var entity in DataList)
            {
                var reflectedId = GetReflectedIdValue(entity);

                if (reflectedId == id)
                {
                    return entity;
                }
            }
            return null;
        }

        /// <summary>
        /// Retrieves all entities.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<T> RetrieveAll()
        {
            return DataList.AsEnumerable();
        }

        /// <summary>
        /// Retrieves many entities using the specified predicate.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        public IEnumerable<T> RetrieveMany(Expression<Func<T, bool>> predicate)
        {
            return DataList.Where(predicate.Compile()).AsEnumerable();
        }

        #endregion

        /// <summary>
        /// Gets the value of the id of the entity using reflection.
        /// </summary>
        private int GetReflectedIdValue(T entity)
        {
            var idPropertyName = string.Concat(entity.GetType().Name.ToLowerInvariant(), "id");

            foreach (var propertyInfo in entity.GetType().GetProperties())
            {
                if (propertyInfo.PropertyType == typeof(int) && propertyInfo.Name.ToLowerInvariant() == idPropertyName)
                {
                    return (int)propertyInfo.GetValue(entity, null);
                }
            }
            return default(int);
        }

        #region Loading Of Dummy Data

        /// <summary>
        /// Loads the dummy data.
        /// </summary>
        private void LoadDummyData()
        {
            LoadCategories(_categories);
            LoadBrands(_brands);
            LoadProducts(_products);
            LoadUsers(_users);
        }

        /// <summary>
        /// Loads the categories.
        /// </summary>
        private void LoadCategories(ICollection<ProductCategory> categories)
        {
            categories.Add(new ProductCategory { ProductCategoryId = 1, CategoryName = "Beverages", Description = "Beverages" });
            categories.Add(new ProductCategory { ProductCategoryId = 2, CategoryName = "BakedGoods", Description = "Baked Goods" });
            categories.Add(new ProductCategory { ProductCategoryId = 3, CategoryName = "Vegetables", Description = "Vegetables" });
            categories.Add(new ProductCategory { ProductCategoryId = 4, CategoryName = "Fruits", Description = "Fruits" });
            categories.Add(new ProductCategory { ProductCategoryId = 5, CategoryName = "Hamburgers", Description = "Hamburgers" });
        }

        /// <summary>
        /// Loads the brands.
        /// </summary>
        private void LoadBrands(ICollection<Brand> brands)
        {
            brands.Add(new Brand { BrandId = 1, BrandName = "Coca-Cola", Manufacturer = "The Coca-Cola Company" });
            brands.Add(new Brand { BrandId = 2, BrandName = "Quick", Manufacturer = "Quick Restaurants"});
            brands.Add(new Brand { BrandId = 3, BrandName = "Chiquita", Manufacturer = "Chiquita" });
            brands.Add(new Brand { BrandId = 4, BrandName = "Delhaize", Manufacturer = "Delhaize" });
        }

        /// <summary>
        /// Loads the products.
        /// </summary>
        private void LoadProducts(ICollection<Product> products)
        {
            var category = _categories.First(e => e.ProductCategoryId == 5);
            var brand = _brands.First(e => e.BrandId == 2);

            var product = new Product
                              {
                                  ProductId = 1,
                                  Name = "Giant",
                                  BrandId = brand.BrandId,
                                  Brand = brand,
                                  ProductCategoryId = category.ProductCategoryId,
                                  ProductCategory = category,
                                  ServingSize = 1,
                                  Calories = 535,
                                  Proteins = 25,
                                  Fat = 37.4,
                                  SaturatedFat = 11.9,
                                  Carbohydrates = 24.5,
                                  Sugar = 4.8,
                                  Fibres = 4.8,
                                  Salt = 1.5
                              };

            //category.Products.Add(product);
            //brand.Products.Add(product);

            products.Add(product);
        }

        /// <summary>
        /// Loads the users.
        /// </summary>
        private void LoadUsers(ICollection<User> users)
        {
            users.Add(new User
                           {
                               UserId = 1, 
                               UserName = "Krikke21",
                               FirstName = "Christof", 
                               LastName = "Lauriers",
                               Email = "christof.lauriers@telenet.be"
                           });
        }


        /// <summary>
        /// Gets a random entity from collection.
        /// </summary>
        private TE GetRandomEntityFromCollection<TE>(ICollection<TE> collection, Random random) where TE : class
        {
            if (collection == null || collection.Count == 0)
                return null;

            var number = random.Next(0, collection.Count - 1);

            return collection.ToList()[number];
        } 

        #endregion
    }
}